﻿////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) GHI Electronics, LLC.
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/* Copyright 2014 Pasi J. Elo, blackdogspark@hotmail.com

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

	   http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 
   This class is heavily modifyed version of GHI's Tween class.
 */

using GHI.Glide.Display;
using Microsoft.SPOT;
using Microsoft.SPOT.Presentation.Media;

namespace PJE.GlideEx
{
	/// <summary>
	/// Holds the steps for different animations.
	/// </summary>
	public struct Steps
	{
		/// <summary>
		/// Steps for Collapse.
		/// </summary>
		public int Collapse { get; set; }

		/// <summary>
		/// Steps for Expand.
		/// </summary>
		public int Expand { get; set; }

		/// <summary>
		/// Steps for FadeIn
		/// </summary>
		public int FadeIn { get; set; }

		/// <summary>
		/// Steps for FdeOut
		/// </summary>
		public int FadeOut { get; set; }
	}

	/// <summary>
	/// Tween Direction.
	/// </summary>
	public enum Direction
	{
		Top,
		Bottom,
		Left,
		Right,
		TopLeft,
		BottomLeft,
		TopRight,
		BottomRight
	}

	public class Tween
	{
		/// <summary>
		/// Indicates the number of steps an animation needs to tween from start to finish.
		/// </summary>
		public static Steps NumSteps;

		/// <summary>
		/// Initializes the Tween class.
		/// </summary>
		/// <remarks>Sets the best number of steps to tween from start to finish based on your GHI Electronics board model.</remarks>
		static Tween()
		{
			// Default is 5
			NumSteps.FadeIn = NumSteps.FadeOut = NumSteps.Collapse = NumSteps.Expand = 5;

			switch (Microsoft.SPOT.Hardware.SystemInfo.SystemID.Model)
			{
				case 5: // EMX
					NumSteps.FadeIn = NumSteps.FadeOut = NumSteps.Collapse = NumSteps.Expand = 7;
					break;

				case 6: // ChipworkX
					NumSteps.FadeIn = NumSteps.FadeOut = NumSteps.Collapse = NumSteps.Expand = 50;
					break;

				case 9: // G400
					NumSteps.Collapse = NumSteps.Expand = 25;
					NumSteps.FadeIn = 35;
					NumSteps.FadeOut = 35;
					break;

				default:
					if (GHI.Glide.Glide.IsEmulator)
						NumSteps.FadeIn = NumSteps.FadeOut = NumSteps.Collapse = NumSteps.Expand = 100;
					break;
			}
		}

		/// <summary>
		/// Calculates a position for each step.
		/// </summary>
		/// <param name="start">Start position.</param>
		/// <param name="end">End position.</param>
		/// <param name="steps">Number of steps.</param>
		/// <returns>An array of steps to move from start to end.</returns>
		public static int[] GetSteps(int start, int end, int steps)
		{
			var values = new int[steps + 1];
			for (var i = 0; i < steps + 1; i++)
			{
				var step1 = end - start;
				var step2 = step1 / steps;
				var step3 = step2 * i;
				var step4 = start + step3;
				values[i] = step4;
			}
			return values;
		}

		/// <summary>
		/// Collapses the window in a specified direction and nulls the Glide.MainWindow.
		/// </summary>
		/// <param name="window">Window to collapse.</param>
		/// <param name="direction">Collapse Direction.</param>
		/// /// <param name="color">Color used to mask out the previous screen image</param>
		public static void Collapse(Window window, Direction direction, Color color)
		{
			GHI.Glide.Glide.MainWindow = null;

			var index = 0;
			int count;
			var bitmap1 = new Bitmap(window.Width, window.Height);
			var bitmap2 = window.Graphics.GetBitmap();
			int[] tweenWidth, tweenHeight, tweenX, tweenY;

			bitmap1.DrawRectangle(color, 0, 0, 0, window.Width, window.Height, 0, 0, color, 0, 0, color, 0, 0, 255);
			var width = bitmap1.Width;
			var height = bitmap1.Height;
			switch (direction)
			{
				case Direction.Left:
					tweenWidth = GetSteps(window.Width, 0, NumSteps.Collapse);
					count = tweenWidth.Length;
					while (index < count)
					{
						GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap1, 0, 0, width, height);
						GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap2, 0, 0, tweenWidth[index], height);
						GHI.Glide.Glide.Screen.Flush();
						index++;
					}
					break;

				case Direction.Top:
					tweenHeight = GetSteps(window.Height, 0, NumSteps.Collapse);
					count = tweenHeight.Length;
					while (index < count)
					{
						GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap1, 0, 0, width, height);
						GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap2, 0, 0, width, tweenHeight[index]);
						GHI.Glide.Glide.Screen.Flush();
						index++;
					}
					break;

				case Direction.Right:
					tweenWidth = GetSteps(window.Width, 0, NumSteps.Collapse);
					tweenX = GetSteps(0, window.Width, NumSteps.Collapse);
					count = tweenX.Length;
					while (index < count)
					{
						GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap1, 0, 0, width, height);
						GHI.Glide.Glide.Screen.DrawImage(tweenX[index], 0, bitmap2, 0, 0, tweenWidth[index], height);
						GHI.Glide.Glide.Screen.Flush();
						index++;
					}
					break;

				case Direction.Bottom:
					tweenHeight = GetSteps(window.Height, 0, NumSteps.Collapse);
					tweenY = GetSteps(0, window.Height, NumSteps.Collapse);
					count = tweenY.Length;
					while (index < count)
					{
						GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap1, 0, 0, width, height);
						GHI.Glide.Glide.Screen.DrawImage(0, tweenY[index], bitmap2, 0, 0, width, tweenHeight[index]);
						GHI.Glide.Glide.Screen.Flush();
						index++;
					}
					break;

				case Direction.TopLeft:
					tweenWidth = GetSteps(window.Width, 0, NumSteps.Collapse);
					tweenHeight = GetSteps(window.Height, 0, NumSteps.Collapse);
					count = tweenHeight.Length;
					while (index < count)
					{
						GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap1, 0, 0, width, height);
						GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap2, 0, 0, tweenWidth[index], tweenHeight[index]);
						GHI.Glide.Glide.Screen.Flush();
						index++;
					}
					break;

				case Direction.BottomLeft:
					tweenWidth = GetSteps(window.Width, 0, NumSteps.Collapse);
					tweenHeight = GetSteps(window.Height, 0, NumSteps.Collapse);
					tweenY = GetSteps(0, window.Height, NumSteps.Collapse);
					count = tweenY.Length;
					while (index < count)
					{
						GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap1, 0, 0, width, height);
						GHI.Glide.Glide.Screen.DrawImage(0, tweenY[index], bitmap2, 0, 0, tweenWidth[index], tweenHeight[index]);
						GHI.Glide.Glide.Screen.Flush();
						index++;
					}
					break;

				case Direction.TopRight:
					tweenWidth = GetSteps(window.Width, 0, NumSteps.Collapse);
					tweenHeight = GetSteps(window.Height, 0, NumSteps.Collapse);
					tweenX = GetSteps(0, window.Width, NumSteps.Collapse);
					count = tweenX.Length;
					while (index < count)
					{
						GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap1, 0, 0, width, height);
						GHI.Glide.Glide.Screen.DrawImage(tweenX[index], 0, bitmap2, 0, 0, tweenWidth[index], tweenHeight[index]);
						GHI.Glide.Glide.Screen.Flush();
						index++;
					}
					break;

				case Direction.BottomRight:
					tweenWidth = GetSteps(window.Width, 0, NumSteps.Collapse);
					tweenHeight = GetSteps(window.Height, 0, NumSteps.Collapse);
					tweenY = GetSteps(0, window.Height, NumSteps.Collapse);
					tweenX = GetSteps(0, window.Width, NumSteps.Collapse);
					count = tweenX.Length;
					while (index < count)
					{
						GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap1, 0, 0, width, height);
						GHI.Glide.Glide.Screen.DrawImage(tweenX[index], tweenY[index], bitmap2, 0, 0, tweenWidth[index], tweenHeight[index]);
						GHI.Glide.Glide.Screen.Flush();
						index++;
					}
					break;
			}
			GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap1, 0, 0, width, height);
			GHI.Glide.Glide.Screen.Flush();
		}

		/// <summary>
		/// Expands the window in a specified direction and sets the window to Glide.MainWindow.
		/// </summary>
		/// <param name="window">Window to Expand.</param>
		/// <param name="direction">Expand Direction.</param>
		/// /// <param name="color">Color used to mask out the previous screen image</param>
		public static void Expand(Window window, Direction direction, Color color)
		{
			GHI.Glide.Glide.MainWindow = null;

			var index = 0;
			var bitmap1 = new Bitmap(window.Width, window.Height);
			var bitmap2 = window.Graphics.GetBitmap();
			int count;
			int[] tweenWidth, tweenHeight, tweenX, tweenY;

			bitmap1.DrawRectangle(color, 0, 0, 0, window.Width, window.Height, 0, 0, color, 0, 0, color, 0, 0, 255);
			var width = bitmap1.Width;
			var height = bitmap1.Height;

			switch (direction)
			{
				case Direction.Left:
					tweenWidth = GetSteps(0, window.Width, NumSteps.Expand);
					count = tweenWidth.Length;
					while (index < count)
					{
						GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap1, 0, 0, width, height);
						GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap2, 0, 0, tweenWidth[index], height);
						GHI.Glide.Glide.Screen.Flush();
						index++;
					}
					break;

				case Direction.Top:
					tweenHeight = GetSteps(0, window.Height, NumSteps.Expand);
					count = tweenHeight.Length;
					while (index < count)
					{
						GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap1, 0, 0, width, height);
						GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap2, 0, 0, width, tweenHeight[index]);
						GHI.Glide.Glide.Screen.Flush();
						index++;
					}
					break;

				case Direction.Right:
					tweenWidth = GetSteps(0, window.Width, NumSteps.Expand);
					tweenX = GetSteps(window.Width, 0, NumSteps.Expand);
					count = tweenX.Length;
					while (index < count)
					{
						GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap1, 0, 0, width, height);
						GHI.Glide.Glide.Screen.DrawImage(tweenX[index], 0, bitmap2, 0, 0, tweenWidth[index], height);
						GHI.Glide.Glide.Screen.Flush();
						index++;
					}
					break;

				case Direction.Bottom:
					tweenHeight = GetSteps(0, window.Height, NumSteps.Expand);
					tweenY = GetSteps(window.Height, 0, NumSteps.Expand);
					count = tweenY.Length;
					while (index < count)
					{
						GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap1, 0, 0, width, height);
						GHI.Glide.Glide.Screen.DrawImage(0, tweenY[index], bitmap2, 0, 0, width, tweenHeight[index]);
						GHI.Glide.Glide.Screen.Flush();
						index++;
					}
					break;

				case Direction.TopLeft:
					tweenWidth = GetSteps(0, window.Width, NumSteps.Expand);
					tweenHeight = GetSteps(0, window.Height, NumSteps.Expand);
					count = tweenHeight.Length;
					while (index < count)
					{
						GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap1, 0, 0, width, height);
						GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap2, 0, 0, tweenWidth[index], tweenHeight[index]);
						GHI.Glide.Glide.Screen.Flush();
						index++;
					}
					break;

				case Direction.BottomLeft:
					tweenWidth = GetSteps(0, window.Width, NumSteps.Expand);
					tweenHeight = GetSteps(0, window.Height, NumSteps.Expand);
					tweenY = GetSteps(window.Height, 0, NumSteps.Expand);
					count = tweenY.Length;
					while (index < count)
					{
						GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap1, 0, 0, width, height);
						GHI.Glide.Glide.Screen.DrawImage(0, tweenY[index], bitmap2, 0, 0, tweenWidth[index], tweenHeight[index]);
						GHI.Glide.Glide.Screen.Flush();
						index++;
					}
					break;

				case Direction.TopRight:
					tweenWidth = GetSteps(0, window.Width, NumSteps.Expand);
					tweenHeight = GetSteps(0, window.Height, NumSteps.Expand);
					tweenX = GetSteps(window.Width, 0, NumSteps.Expand);
					count = tweenX.Length;
					while (index < count)
					{
						GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap1, 0, 0, width, height);
						GHI.Glide.Glide.Screen.DrawImage(tweenX[index], 0, bitmap2, 0, 0, tweenWidth[index], tweenHeight[index]);
						GHI.Glide.Glide.Screen.Flush();
						index++;
					}
					break;

				case Direction.BottomRight:
					tweenWidth = GetSteps(0, window.Width, NumSteps.Expand);
					tweenHeight = GetSteps(0, window.Height, NumSteps.Expand);
					tweenY = GetSteps(window.Height, 0, NumSteps.Expand);
					tweenX = GetSteps(window.Width, 0, NumSteps.Expand);
					count = tweenX.Length;
					while (index < count)
					{
						GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap1, 0, 0, width, height);
						GHI.Glide.Glide.Screen.DrawImage(tweenX[index], tweenY[index], bitmap2, 0, 0, tweenWidth[index], tweenHeight[index]);
						GHI.Glide.Glide.Screen.Flush();
						index++;
					}
					break;
			}
			GHI.Glide.Glide.MainWindow = window;
		}

		/// <summary>
		/// Fades the window out and nulls the Glide.MainWindow.
		/// </summary>
		/// <param name="window">Window to fade.</param>
		/// /// <param name="color">Color used to mask out the previous screen image</param>
		public static void FadeOut(Window window, Color color)
		{
			GHI.Glide.Glide.MainWindow = null;

			var alpha = GetSteps(window.Alpha, 0, NumSteps.FadeOut);
			var index = 0;
			var bitmap1 = new Bitmap(window.Width, window.Height);
			var bitmap2 = window.Graphics.GetBitmap();

			bitmap1.DrawRectangle(color, 0, 0, 0, window.Width, window.Height, 0, 0, color, 0, 0, color, 0, 0, 255);
			var width = bitmap1.Width;
			var height = bitmap1.Height;
			var count = alpha.Length;

			while (index < count)
			{
				GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap1, 0, 0, width, height);
				GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap2, 0, 0, width, height, (ushort)alpha[index]);
				GHI.Glide.Glide.Screen.Flush();
				index++;
			}
		}

		/// <summary>
		/// Fades the window in and sets the window to Glide.Maindow.
		/// </summary>
		/// <param name="window">Window to fade.</param>
		/// /// <param name="color">Color used to mask out the previous screen image</param>
		public static void FadeIn(Window window, Color color)
		{
			GHI.Glide.Glide.MainWindow = null;

			var alpha = GetSteps(0, window.Alpha, NumSteps.FadeIn);

			var index = 0;
			var bitmap1 = new Bitmap(window.Width, window.Height);
			var bitmap2 = window.Graphics.GetBitmap();

			bitmap1.DrawRectangle(color, 0, 0, 0, window.Width, window.Height, 0, 0, color, 0, 0, color, 0, 0, 255);
			var width = bitmap1.Width;
			var height = bitmap1.Height;
			var count = alpha.Length;
			while (index < count)
			{
				GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap1, 0, 0, width, height);
				GHI.Glide.Glide.Screen.DrawImage(0, 0, bitmap2, 0, 0, width, height, (ushort)alpha[index]);
				GHI.Glide.Glide.Screen.Flush();
				index++;
			}
			GHI.Glide.Glide.MainWindow = window;
		}
	}
}
